#!/usr/local/bin/perl

$Version = "1.1";
printf "ABC2Tab Version $Version, Copyright (C) 1998  Lyle Melick\n";

############################################################################
#
# abc2tab: Program to convert tunes in abc format to ASCII Tablature.
# Copyright (C) 1998  Lyle Melick
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# The author can be contacted as follows:
#
# Lyle Melick
# melick@centuryinter.net
#
############################################################################

##-----
#
# parse command line
#
#-----
print "ARGV = ", join(" ", @ARGV), "\n" if $verbose;
&Getopts('d:u:x:fghsv');
if ($opt_h || $opt_err) {
    print "Usage: $0 [-d[#]] [-f] [-g] [-h] [-s] [-u[#]] [-v] [-x[xref]] [abcfile.abc]\n";
    print " -d -- adjust DOWN by this many octaves (Example: -d1)\n";
    print " -f -- suppress footer annotations\n";
    print " -g -- guitar style tab (High string on top)\n";
    print " -h -- print this help\n";
    print " -s -- suppress interline spaces, add column for spaces in source\n";
    print " -u -- adjust UP by this many octaves (Example: -u1)\n";
    print " -v -- verbose\n";
    print " -x -- single x reference to print\n";
    exit 0;
}

$Footers = 1;
$Footers = 0 if $opt_f;
$GuitarTab = 0;
$GuitarTab = 1 if $opt_g;
$Melody_Only = 0;
$Melody_Only = 1 if $opt_m;
$Spaces = 1;
$Spaces = 0 if $opt_s;
$verbose = 0;
$verbose = 1 if $opt_v;
$Xref_List = $opt_x if ($opt_x ne "");
printf "\tVERBOSE:Footers=%s,GuitarTab=%s,Melody_Only=%s,RegKey=%s,verbose=%s,Xref_List=%s\n", $Footers, $GuitarTab, $Melody_Only, $opt_r, $verbose, $Xref_List if $verbose;

#-----
#
# Setup
#
#-----
printf "\tVERBOSE:Initializing\n" if $verbose;

$NoteXlt = ('CDEFGABcdefgab zZ');
@NoteNum = ('40','42','44','45','47','49','51','52','54','56','57','59','61','63',' ','99','99');
@Frets = ('-0-','-%-','-1-','-1%','-2-','-3-','-3%','-4-','-4%','-5-','-6-','-6%','-7-','-7%','-8-','-8%','-9-','-10','10%','-11','11%','-12','-13','13%','-14') if !$GuitarTab;
@Frets = ('-0-','-1-','-2-','-3-','-4-','-5-','-6-','-7-','-8-','-9-','-10','-11','-12','-13','-14','-15','-16','-17','-18','-19','-20','-21','-22','-23','-24') if $GuitarTab;

@NoteLnth8  = (' t','dt',' s',' e',' q','dq',' h','5X','dh','7X',' w','  ');
@NoteLnth16 = (' x','dx',' t',' s',' e','de',' q','5X','dq','7X',' h','  ');

$startabcline = 0;
$endabcline = 0;
$abcline = 0;
$abcnote = 0;

printf "\tVERBOSE:All set...\n" if $verbose;

#-----
#
# Open the File
#
#-----
printf "\tVERBOSE:Opening input file\n" if $verbose;
open(FILE, "@ARGV") || die "Can't open file - @ARGV: $!\n";

#-----
#
# Begin Processing the file
#
#-----
while (<FILE>) {
    chop;
# checking for and handling blank line between songs.
    if ($_ eq "") {
	if ($InSong eq "Yes") {
	    $endabcline = $abcline;
	    SWITCH: for ($X_Number) {
		if ($X_Number == $Xref_List || $Xref_List == "") { &Process($startabcline,$endabcline); };
	    };
	    $startabcline = $endabcline + 1;
	    $InSong = "No";
	} else {
	    next;
	}
    }
    (@inline0) = split(/\!/, $_);
    $inline0 = $inline0[0];
    
    (@inline1) = split(/\\/, $inline0);
    $inline1 = $inline1[0];
    
    (@inline2) = split(/\*/, $inline1);
    $inline2 = $inline2[0];
    
    (@inline) = split(/%/, $inline2);
    $inline = $inline[0];
    (@Field) = split(/\:/,$inline[0]);
    (@RDef) = split(/\:/,$inline[2]);
    if ($inline[0] eq "" && $inline[1] eq "" && $RDef[0] eq "ABC2TAB") {
	printf "\tVERBOSE:$RDef[1], $RDef[2]\n" if $verbose;
	&ReadDefinitions($RDef[1], $RDef[2]);
    }
    
#--------------------
#
# Process Fields
#
#--------------------
    if (length($Field[0]) eq 1) {
	$InHistory = "No";
	if (substr($Field[1],0,1) eq " ") {
	    $Field[1] = substr($Field[1],1,length($Field[1])-1);
	}
	
#--------------------
#
# Handle fields
#
#--------------------
	printf "\tVERBOSE:Parsing Header...\n" if $verbose;
	SWITCH: for ($Field[0]) {
	    /[Aa]/ && do { $information[1]  = $Field[1]; last SWITCH; };
	    /[Bb]/ && do { $information[2]  = "From The Book $Field[1]"; last SWITCH; };
	    /[Cc]/ && do { $information[3]  = $Field[1]; last SWITCH; };
	    /[Dd]/ && do { $information[4]  = "Recorded on $Field[1]"; last SWITCH; };
	    /[Ee]/ && do { $information[5]  = "Elemskip = $Field[1]"; last SWITCH; };
	    /[Ff]/ && do { $information[6]  = "Processed from File $Field[1]"; last SWITCH; };
	    /[Gg]/ && do { $information[7]  = "Group: $Field[1]"; last SWITCH; };
	    /[Hh]/ && do { $information[8]  = "History: $Field[1]"; $InHistory = "Yes"; last SWITCH; };
	    /[Ii]/ && do { $information[9]  = "Information: $Field[1]"; last SWITCH; };
	    /[Jj]/ && do { $information[10] = $Field[1]; last SWITCH; };
	    /[Kk]/ && do {
			     if ($InHeader eq "No") {
				 @part[$abcline+1] = "Key of $Field[1]";
				 &SetKey($Field[1]);
			     } else {
				 printf OUT "\tKey of %s                                                   %s\n\n       ", $Field[1], $information[3];
				 &SetKey($Field[1]);
				 if ($HaveStringDef eq "No") {
				     $RDef[1] = "D A d";
				     $RDef[2] = "Default Dulcimer Tuning";
				     &ReadDefinitions($RDef[1],$RDef[2]);
				 }
				 if ($HaveTimeSig eq "No") {
				     $TimeSig[0] = 4;
				     $TimeSig[1] = 4;
				 }
				 $InHeader = "No";
				 printf "\tVERBOSE:Header complete, starting notes\n" if $verbose;
			     }
			     last SWITCH; 
			 };
	    /[Ll]/ && do {
			     (@DefLnth) = split(/\//,$Field[1]);
			     $DefNoteLnth = @DefLnth[1];
			     $LnthExpl = 1;
			     last SWITCH; 
			 };
	    /[Mm]/ && do {
			     $HaveTimeSig = "Yes";
			     (@TimeSig) = split(/\//,$Field[1]);
			     if (@TimeSig[0] eq "C" || @TimeSig[0] eq "c") {
				 $TimeSig[0] = 4;
				 $TimeSig[1] = 4;
			     }
			     if (@TimeSig[0] eq "C|" || @TimeSig[0] eq "c|") {
				 $TimeSig[0] = 2;
				 $TimeSig[1] = 4;
			     }
			     if ($LnthExpl = 0) {
				 if (($TimeSig[0] / $TimeSig[1]) >= .75) {
				     $DefNoteLnth = 8;
				 } else {
				     $DefNoteLnth = 16;
				 }
			     }
			     last SWITCH; 
			};
	    /[Nn]/ && do { $information[14] = "Notes: $Field[1]"; last SWITCH; };
	    /[Oo]/ && do { $information[15] = "Origin: $Field[1]"; last SWITCH; };
	    /[Pp]/ && do {
			    if ($InHeader eq "No") {
				@part[$abcline+1] = "($Field[1])";
			    } else {
				printf OUT "\n\tPlay parts in following order: %s\n\n", $Field[1];
			    }
			    last SWITCH; 
			};
	    /[Qq]/ && do { printf OUT "\t%s\n", $Field[1]; last SWITCH; };
	    /[Rr]/ && do { printf OUT "\t%s\n", $Field[1]; last SWITCH; };
	    /[Ss]/ && do { $information[19] = "Source: $Field[1]"; last SWITCH; };
	    /[Tt]/ && do {
			    if ($NoTitle eq "TRUE") {
				$LnthTitle = length($Field[1]);
				printf OUT ' ' x (40 - ($LnthTitle / 2));
				printf OUT "%s\n\n     ", $Field[1];
				$NoTitle = "FALSE";
			    } else {
				$LnthTitle = length($Field[1]);
				printf OUT ' ' x (35 - ($LnthTitle / 2));
				printf OUT "%s\n     ", $Field[1];
			    }
			    last SWITCH; 
			};
	    /[Uu]/ && do { $information[21] = $Field[1]; last SWITCH; };
	    /[Vv]/ && do { $information[22] = "Voice: $Field[1]"; last SWITCH; };
	    /[Ww]/ && do { $information[23] = "Lyrics: $Field[1]"; last SWITCH; };
	    /[Xx]/ && do { 
			    &Initialize();
			    $X_Number = $Field[1];
			    $information[24] = "Song #$Field[1]";
			    SWITCH: for ($X_Number) {
				close(OUT);
				if ($X_Number == $Xref_List || $Xref_List == "") {
				    printf "Creating %s.tab from %s\n", $Field[1], @ARGV;
				    open(OUT,">$Field[1].tab") || die "Can't open file - $Field[1].tab: $!\n";
				};
			    };
			    $NoTitle = "TRUE";
			    $InHeader = "Yes";
			    $InSong = "Yes";
			    $HaveStringDef = "No";
			    last SWITCH; 
			};
	    /[Yy]/ && do { $information[25] = $Field[1]; last SWITCH; };
	    /[Zz]/ && do { $information[26] = "Transcription Note: $Field[1]"; last SWITCH; };
	};

    } else {
	next if ($InSong ne "Yes");
#--------------------
#
# process music
#
#--------------------
	chop;
	if ($InHistory eq "Yes") {$information[8] .= "\n         $inline"; next;};
	
#--------------------
#
# abc2ps comment handling
#
#--------------------
	if ($inline[0] eq "" && $inline[1] eq "" && $RDef[0] eq "begintext") {
	    $InComment = "Yes";
	}
	if ($inline[0] eq "" && $inline[1] eq "" && $RDef[0] eq "endtext") {
	    $InComment = "No";
	}
	next if ($InComment eq "Yes");
#--------------------
#
# end of abc2ps comment handling
#
#--------------------
	$NumChars = length($inline);
	printf "\tVERBOSE:INPUT LINE:$inline\n" if $verbose;
	printf "\tVERBOSE:NumChars=$NumChars.\n" if $verbose;
	for ($nt=0; $nt <= $NumChars; $nt++) {
	    @Chord{join('.',$abcline,$abcnote)} = "";
	    $InTuple = "No";
	    $Gracing = "No";
	    $Lnth = 1;
	    $Note = substr($inline,$nt,1);
	    printf "\tVERBOSE:Processing NOTE:$Note-nt=$nt\n" if $verbose;
#
# Handle Guitar Chords
#
	    if (($InChord eq "Yes") && ( $Note ne '"'))  { @Chord{join('.',$abcline,$abcnote)} .= $Note; };
	    next if (($InChord eq "Yes") && ( $Note ne '"'));
#
#Handle Notes & modifiers
#
	    if ($nt < $NumChars - 1) {    
		SWITCH: for (substr($inline,$nt+1,1)) {
		    /\(/           && do { $ProcessNow = "Yes"; };
		    /\|/           && do { $ProcessNow = "Yes"; };
		    /\:/           && do { $ProcessNow = "Yes"; };
		    /\[/           && do { $ProcessNow = "Yes"; };
		    / /            && do { $ProcessNow = "Yes"; };
		    /[A-Ga-gZz]/   && do { $ProcessNow = "Yes"; };
		};
	    }
	    if ($nt == $NumChars - 1) { $ProcessNow = "Yes"; };   

	    SWITCH: for ($Note) {
#Notes
		/[A-Ga-g]/    && do {                                                                       $CurrentNote = $Note; printf "1-%s ", $Note if $verbose; last SWITCH; };
#Accidentals
		/\^/          && do { $Accidental++;                                                                              printf "2-%s ", $Note if $verbose; last SWITCH; };
		/\_/          && do { $Accidental--;                                                                              printf "2-%s ", $Note if $verbose; last SWITCH; };
		/\=/          && do { $Accidental = 0;                                                                            printf "2-%s ", $Note if $verbose; last SWITCH; };
#Octave Modifiers
		/\,/          && do { $Octave = $Octave - 12;                                                                     printf "3-%s ", $Note if $verbose; last SWITCH; };
		/\'/          && do { $Octave = $Octave + 12;                                                                     printf "3-%s ", $Note if $verbose; last SWITCH; };
#Rests
		/[zZ]/        && do {                                                                       $CurrentNote = $Note; printf "4-%s ", $Note if $verbose; last SWITCH; };
#Spaces
		/ /           && do { $Lnth = 9;                                                            $CurrentNote = $Note; printf "5-%s ", $Note if $verbose; last SWITCH; };
#Barlines
		/\|/          && do { $Lnth = 9; &CheckEnding;                                              $CurrentNote = $Note; printf "5-%s ", $Note if $verbose; last SWITCH; };
		/\:/          && do { $Lnth = 9;                                                            $CurrentNote = $Note; printf "5-%s ", $Note if $verbose; last SWITCH; };
#Slurs
		/\(/          && do {
					 $InSlur = "Yes";
					 SWITCH: for (substr($inline,$nt+1,1)) {
					     /[1-9]/ && do  { $InTuple = "Yes";
							      $TupleLength = "tp";
							      $TupleLength .= substr($inline,$nt+1,1);
							      printf "6a-%s ", $Note if $verbose;
							      $nt++;
							      last SWITCH;
							    };
					 }
					 printf "6-%s ", $Note if $verbose;
					 last SWITCH;
				    };
		/\)/          && do { $InSlur = "No";                                                                          printf "6-%s ", $Note if $verbose; last SWITCH; };
#Ties
		/\-/          && do {                                                                                          printf "6-%s ", $Note if $verbose; last SWITCH; };
#Stacatto
		/\./          && do {                                                                                          printf "6-%s ", $Note if $verbose; last SWITCH; };
#Bowings
		/u/           && do {                                                                                          printf "6-%s ", $Note if $verbose; last SWITCH; };
		/v/           && do {                                                                                          printf "6-%s ", $Note if $verbose; last SWITCH; };
#Misc Decorations - Ignored
		/J/           && do {                                                                                          printf "6-%s ", $Note if $verbose; last SWITCH; };
		/M/           && do {                                                                                          printf "6-%s ", $Note if $verbose; last SWITCH; };
		/H/           && do {                                                                                          printf "6-%s ", $Note if $verbose; last SWITCH; };
		/R/           && do {                                                                                          printf "6-%s ", $Note if $verbose; last SWITCH; };
		/T/           && do {                                                                                          printf "6-%s ", $Note if $verbose; last SWITCH; };
#Length Modifiers
		/>/           && do { $Lnth++;                                                                                 printf "7-%s ", $Note if $verbose; last SWITCH; };
		/</           && do { $Lnth--;                                                                                 printf "7-%s ", $Note if $verbose; last SWITCH; };
		/\//          && do { $Lnth--;                                                                                 printf "7-%s ", $Note if $verbose; last SWITCH; };
		/[1-9]/       && do { $Lnth=$Note;                                                                             printf "7-%s ", $Note if $verbose; last SWITCH; };
#Gracings
		/\~/          && do { $Gracing = "Yes";                                                                        printf "8-%s ", $Note if $verbose; last SWITCH; };
		/\{/          && do { $Gracing = "Yes";                                                                        printf "8-%s ", $Note if $verbose; last SWITCH; };
		/\}/          && do {                                                                                          printf "8-%s ", $Note if $verbose; last SWITCH; };
#Old Style Chords
		/\+/          && do { if ($InMultNote eq "Yes") {
												       
										   $InMultNote = "No";
								} else {
										   $InMultNote = "Yes";
								};
															       printf "9-%s ", $Note if $verbose; last SWITCH; };
#New Style Chords
		/\[/          && do {     $InMultNote = "Yes";                                                                 printf "A-%s ", $Note if $verbose; last SWITCH; };
		/\]/          && do {     $InMultNote = "No";                                                                  printf "A-%s ", $Note if $verbose; last SWITCH; };
#Guitar/Accompaniment Chords
		/\"/          && do { if ($InChord eq "Yes") {
								  $InChord = "No";
								  if (length(@Chord{join('.',$abcline,$abcnote)}) > 3) {
								      $NumSpaces{join('.',$abcline,$abcnote)} = length(@Chord{join('.',$abcline,$abcnote)}) - 3;
								  }
								  if (length(@Chord{join('.',$abcline,$abcnote)}) < 3) {
								      @Chord{join('.',$abcline,$abcnote)} .= " " x (3 - length(@Chord{join('.',$abcline,$abcnote)}));
								  }
							      } else {
								  $InChord = "Yes";
								  $NumSpaces{join('.',$abcline,$abcnote)} = 0;
							      };
				      printf "B-%s ", $Note if $verbose;
				      last SWITCH;
				    };
#Misc Ignored
		/h-twyIK-LN-SU-Y/ && do {                                                                                      printf "C-%s ", $Note if $verbose; last SWITCH; };
	    };

#Put it all together

	    next if ($ProcessNow ne "Yes");
	    @note{join('.',$abcline,$abcnote)} = $CurrentNote;
	    if ($InTuple eq "Yes") { $Lnth = $TupleLength; };
	    @lnth{join('.',$abcline,$abcnote)} = $Lnth;
	    @Grace{join('.',$abcline,$abcnote)} = $Gracing;
	    @accd{join('.',$abcline,$abcnote)} = $Accidental;
	    $Octave = $Octave + ($opt_u*12) - ($opt_d*12);
	    @octv{join('.',$abcline,$abcnote)} = $Octave;
	    @Ending{join('.',$abcline,$abcnote)} = $EndingNo;
	    $abcnote++;
	    $Accidental = 0;
	    $Octave = 0;
	    $ProcessNow = "No";
	}
    }
#-----
#
# End of an ABC text file line
#
#-----
    printf "\tVERBOSE:There were $abcnote notes in line $abcline\n" if $verbose;
    $abcnote{$abcline} = $abcnote;
    $abcline++;
    $abcnote=0;
}
#-------------------
#
# Handle missing blank line at end of file
#
#-------------------
if ($InSong eq "Yes") {
    $endabcline = $abcline;
    SWITCH: for ($X_Number) {
	if ($X_Number == $Xref_List || $Xref_List == "") { &Process($startabcline,$endabcline); };
    };
}
printf "\tVERBOSE:End of File\n" if $verbose;
close(FILE);
exit 0;

##-----
#
# End of Song - Begin processing output
#
#-----
sub Process {
printf "\tVERBOSE:Song Parsed...Starting processing\n" if $verbose;
for ($abcline=$startabcline; $abcline < $endabcline; $abcline++) {
    printf "\tVERBOSE:Processing line %s\n", $abcline if $verbose;
    for ($tabline=0; $tabline <= (2*$NumStrings)+4; $tabline++) {
	&CheckTabline($tabline,$TabLineStatus);
	$String_Note = $StringNoteNumber[$StringNumber];
	printf "\tVERBOSE: This string is number: %s, Note: %s\n", $StringNumber, $StringNote if $verbose;
	next if (($TabLineStatus eq "Space") && ($Spaces == 0));
	for ($abcnote=0; $abcnote < $abcnote{$abcline}; $abcnote++) {
#--------------------
#
# Initialize & load data for current pass
#
#--------------------
	    $Note = @note{join('.',$abcline,$abcnote)};
	    next if ($Note eq " " && $Spaces == 0);
	    $Accidental = $accd{join('.',$abcline,$abcnote)};
	    $Octave = $octv{join('.',$abcline,$abcnote)};
	    $EndingNo = @Ending{join('.',$abcline,$abcnote)};
	    $Gracing = $Grace{join('.',$abcline,$abcnote)};
	    $Lnth = @lnth{join('.',$abcline,$abcnote)};
#--------------------
#
# Collect Used Notes
#
#--------------------
	    @UsedNotes[($NoteNum[index($NoteXlt,$Note)] + $Octave + $Accidental + $KeyNotes[index($NoteXlt,$Note)] - 1)] = 1;
#--------------------
#
# Handle Misc Templates
#
#--------------------
	    printf "\tVERBOSE:Handling note between periods .$Note." if $verbose;
	    SWITCH: for ($Note) {
		/\|/   && do { $Tab = @MsrTmplat[$tabline]; $Tab =~ s/E/$EndingNo/g; last SWITCH; };
		/\:/   && do { $Tab = @RptTmplat[$tabline];                          last SWITCH; };
		/ /    && do { $Tab = @SpcTmplat[$tabline];                          last SWITCH; };
		/[Zz]/ && do { $Tab = @RestTmplat[$tabline];                         last SWITCH; };
		$Tab = @TabTmplat[$tabline];
	    };
#--------------------
#
# Handle Note Lengths
#
#--------------------
	    if ($TabLineStatus eq "Time") {
		if (substr(@lnth{join('.',$abcline,$abcnote)},0,2) eq "tp") {
		    $baT = substr(@lnth{join('.',$abcline,$abcnote)},2,1);
		    $Tab =~ s/TT /.T./g;
		    $Tab =~ s/T/$baT/g;
		} else {
		    if ($DefNoteLnth eq "16") {
			$Tab =~ s/TT/@NoteLnth16[(@lnth{join('.',$abcline,$abcnote)}) + $DefLnthOff]/g;
			} else {
			$Tab =~ s/TT/@NoteLnth8[(@lnth{join('.',$abcline,$abcnote)}) + $DefLnthOff]/g;
		    }
		}
	    }
#--------------------
#
# Handle Notes
#
#--------------------
	    if ($TabLineStatus eq "Space") {
	    }
	    if ($TabLineStatus eq "Line") {
		$Absolute_Note = ($NoteNum[index($NoteXlt,$Note)] + $Octave + $Accidental + $KeyNotes[index($NoteXlt,$Note)] - 1);
		$Diff_Note = $Absolute_Note - $String_Note;

		if ($Gracing eq "Yes") {
		    $Tab =~ s/-/~/g;
		}
		SWITCH: for ($Diff_Note) {
		    (($Diff_Note <  0) && ($tabline == $BottomLine))                      && do { $Tab =~ s/W/-*-/g;                last SWITCH; };
		    (($Diff_Note <  0) && ($tabline != $BottomLine))                      && do { $Tab =~ s/W/---/g;                last SWITCH; };
		    (($Diff_Note >= 0) && ($Diff_Note <  $StringDelt[$StringNumber + 1])) && do { $Tab =~ s/W/$Frets[$Diff_Note]/g; last SWITCH; };
		    (($Diff_Note >= 0) && ($Diff_Note >= $StringDelt[$StringNumber + 1])) && do { $Tab =~ s/W/---/g;                last SWITCH; };
		};
	    }
	    @tab{join('.',$tabline,$abcline,$abcnote)} = $Tab;
	}
    }
}
#-----
#
# Print out the used notes.
#
#-----
printf "\tVERBOSE:Used Notes:\n" if $verbose;
for ($i=1; $i<=88; $i++) {
    if ($UsedNotes[$i] == 1) {
	printf "%s-%s,", $i,$UsedNotes[$i] if $verbose;
    }
}
#printf ":End of Used Notes:\n" if $verbose;
#-----
#
# Print it out.
#
#-----
    SWITCH: for ($X_Number) {
	if ($X_Number == $Xref_List || $Xref_List == "") { &PrintOut($startabcline,$endabcline); };
    };
} # Process

##--------------------
#
# Print song
#
#--------------------
sub PrintOut {
for ($abcline=$startabcline; $abcline < $endabcline; $abcline++) {
    next if $abcnote{$abcline} eq "0";
    for ($tabline=0; $tabline <= ((2*$NumStrings)+4); $tabline++) {
	&CheckTabline($tabline,$TabLineStatus);
	next if (($TabLineStatus eq "Space") && ($Spaces == 0));
	#
	# Time Signature
	#
	$Tab = @TimeTmplat[$tabline];
	if ($tabline == 2)                   { $Tab =~ s/V/@TimeSig[0]/g; }
	if ($tabline == (2*$NumStrings - 2)) { $Tab =~ s/V/@TimeSig[1]/g; }
	if ($tabline == $NumStrings)         { $Tab =~ s/V/\//g; }
	$Tab =~ s/V/ /g;
	#
	# String Names
	#
	if ($TabLineStatus eq "Line") {
	    SWITCH: for (length(@StringDef[$StringNumber])) {
		/1/ && do { $tString =         "t"; last SWITCH; };
		/2/ && do { $tString =        " t"; last SWITCH; };
		/3/ && do { $tString =       "  t"; last SWITCH; };
		/4/ && do { $tString =      "   t"; last SWITCH; };
		/5/ && do { $tString =     "    t"; last SWITCH; };
		/6/ && do { $tString =    "     t"; last SWITCH; };
		/7/ && do { $tString =   "      t"; last SWITCH; };
		/8/ && do { $tString =  "       t"; last SWITCH; };
		/9/ && do { $tString = "        t"; last SWITCH; };
	    };
	    $Tab =~ s/$tString/@StringDef[$StringNumber]/g;
	}
	#
	# Part/Letter numbers above staff
	#
	if (@part[$abcline] ne "") {
	    $ShortPart  = "(";
	    $ShortPart .= substr(@part[$abcline],1,1);
	    $ShortPart .= ")";
	    $Tab =~ s/www/$ShortPart/g;
	} else {
	    $Tab =~ s/www/   /g;
	}
	printf OUT "%s", $Tab;
	for ($abcnote=0; $abcnote <= $abcnote{$abcline}; $abcnote++) {
	    $Tab = @tab{join('.',$tabline,$abcline,$abcnote)};
	    if ($tabline eq "1") { printf "\tVERBOSE:Printing Note: $abcnote $Tab\n" if $verbose; }
	    next if $Tab eq "";
	    if (@Chord{join('.',$abcline,$abcnote)} eq "" ) {
		@Chord{join('.',$abcline,$abcnote)} = "   ";
	    }
	    $Tab =~ s/C/@Chord{join('.',$abcline,$abcnote)}/g;
	    printf OUT "%s", $Tab;
	    if (($NumSpaces{join('.',$abcline,$abcnote)} > 0) && ($tabline != 0)) {
		if ($TabLineStatus eq "Line") {
		    printf OUT "-" x ($NumSpaces{join('.',$abcline,$abcnote)});
		    } else {
		    printf OUT " " x ($NumSpaces{join('.',$abcline,$abcnote)});
		}
	    }
	}
	printf OUT "\n       ";
    }
}
SWITCH: for ($X_Number) {
    if ($X_Number == $Xref_List || $Xref_List == "") { &PrintFoot(); };
};
} # PrintOut

##--------------------
#
# Print Footer
#
#--------------------
sub PrintFoot {
printf OUT "\nTAB for %s\nProcessed by ABC2TAB Version %s, Copyright 1998, Lyle Melick\n\n", $InstDesc, $Version;
if ($Footers == 1) {
    printf "\tVERBOSE:Printing Footer\n" if $verbose;
    printf OUT "The rhythmic notation used works as follows :\n";
    printf OUT "w = whole note (semibreve)          h = half note (minim)\n";
    printf OUT "q = quarter note (crotchet)         e = eigth note (quaver)\n";
    printf OUT "s = sixteenth note (semiquaver)     t = thirtysecond note (demisemiquaver)\n";
    printf OUT "dh = dotted half note              dq = dotted quarter note\n";
    printf OUT "de = eighth note\n\n";
    printf OUT "Other Notation (In tab with fret numbers) :\n";
    printf OUT "h = hammer on                       / = slide up\n";
    printf OUT "p = pull off                        \\ = slide down\n";
    printf OUT "b = bend up                         v = vibrato\n";
    printf OUT "r = release bend                    t = Right Hand tap\n";
    printf OUT "<N> = Harmonic on N fret            x = Heavy Damp\n\n";
    printf OUT "A percent sign beside a fret number signifies a half fret.\n\n";
    printf OUT "An Asterisk (*) on a string line signifies the note is too low\n";
    printf OUT "   or too high for the instrument\n\n";
}
printf OUT "Other stuff from abc header:\n";

for ($info=1; $info <= 26; $info++) {
    if ($information[$info] ne "") {
	printf OUT "%s\n", $information[$info];
    }
}
printf OUT "\f";
} # PrintFoot

##--------------------
#
# Misc routines
#
#----------------------
##--------------------
#
# Check for Endings
#
#----------------------
sub CheckEnding {
    $EndingNo = " ";
    SWITCH: for (substr($inline,$nt+1,1)) {
	/[1-9]/   && do { $EndingNo = substr($inline,$nt+1,1); };
    };
    return 1;
} # CheckEnding

##--------------------
#
# Check tabline for line/space/chord/time/text
#
#----------------------
sub CheckTabline {

    SWITCH: for ($tabline) {
       if ($tabline == (0))             { $TabLineStatus = "Chord"; $StringNumber = -1; last SWITCH; };
       if ($tabline == (2*$NumStrings)) { $TabLineStatus = "Time";  last SWITCH; };
       if ($tabline >  (2*$NumStrings)) { $TabLineStatus = "Text";  last SWITCH; };
       if (($tabline < (2*$NumStrings)) && ($tabline == 1 || $tabline == 3 || $tabline == 5 || $tabline == 7 || $tabline == 9 || $tabline == 11)) { $TabLineStatus = "Line";  $StringNumber++; last SWITCH; } else { $TabLineStatus = "Space";  last SWITCH; };
    };

} # CheckTabline

##--------------------
#
# Set up tablature Templates
#
#----------------------
sub SetTab {
printf "\tVERBOSE:Setting up TAB Templates\n" if $verbose;
for ($ns=0; $ns < ($NumStrings*2)+4; $ns++) {
    SWITCH: for ($ns) {
	($ns eq "0") && do {
	    @TabTmplat[$ns]  = (' C');
	    @TimeTmplat[$ns] = ('   www');
	    @SpcTmplat[$ns]  = (' ');
	    @RestTmplat[$ns] = ('   ');
	    @MsrTmplat[$ns]  = ('E');
	    @RptTmplat[$ns]  = ('  ');
	    last SWITCH;
	};
	($ns eq "1" || $ns eq "3" || $ns eq "5" || $ns eq "7" || $ns eq "9" || $ns eq "11") && do {
	    @TabTmplat[$ns]  = ('-W');
	    @TimeTmplat[$ns] = ('    t|');
	    @SpcTmplat[$ns]  = ('-');
	    @RestTmplat[$ns] = ('---');
	    @MsrTmplat[$ns]  = ('|');
	    @RptTmplat[$ns]  = ('--');
	    last SWITCH;
	};
	($ns eq "2" || $ns eq "4" || $ns eq "6" || $ns eq "8" || $ns eq "10") && do {
	    @TabTmplat[$ns]  = ('    ');
	    @TimeTmplat[$ns] = (' V   |');
	    @SpcTmplat[$ns]  = (' ');
	    @RestTmplat[$ns] = ('   ');
	    @MsrTmplat[$ns]  = ('|');
	    @RptTmplat[$ns]  = (' *');
	    last SWITCH;
	};
    }
    if ($ns == ($NumStrings*2)-4) {
	@RestTmplat[$ns] = (' R ');
    }
    if ($ns == ($NumStrings*2)-3) {
	@RestTmplat[$ns] = ('-e-');
    }
    if ($ns == ($NumStrings*2)-2) {
	@RestTmplat[$ns] = (' s ');
    }
    if ($ns == ($NumStrings*2)-1) {
	@RestTmplat[$ns] = ('-t-');
    }
    if ($ns == ($NumStrings*2)) {
	@TabTmplat[$ns]  = (' TT ');
	@TimeTmplat[$ns] = ('      ');
	@SpcTmplat[$ns]  = (' ');
	@RestTmplat[$ns] = ('TT ');
	@MsrTmplat[$ns]  = (' ');
	@RptTmplat[$ns]  = ('  ');
    }
    if ($ns > ($NumStrings*2)) {
	@TabTmplat[$ns]  = ('    ');
	@TimeTmplat[$ns] = ('      ');
	@SpcTmplat[$ns]  = (' ');
	@RestTmplat[$ns] = ('   ');
	@MsrTmplat[$ns]  = (' ');
	@RptTmplat[$ns]  = ('  ');
    }
}
} # SetTab

##--------------------
#
# Routines listed
# from here to end are
# run once per song
#
#----------------------
##--------------------
#
# Set up Key Signature Related Stuff
#
#----------------------
sub SetKey {
printf "\tVERBOSE:Handling Key Signature\n" if $verbose;
#This table is setup for addition in the line 5,3,1 translations that handle octaves & accidentals.
#I am subtracting one from the note number in the translation, so that a natural is a plus one here.
SWITCH: for ($Field[1]) {
    /C#|C#Maj|D#Dor|E#Phr|F#Lyd|G#Mix|A#m|A#Min|A#Aeo|B#Loc/   && do { @KeyNotes = ('2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', ' ',' ',' '); last SWITCH; };
    /F#|F#Maj|G#Dor|A#Phr|B#Lyd|C#Mix|D#m|D#Min|D#Aeo|E#Loc/   && do { @KeyNotes = ('2', '2', '2', '2', '2', '2', '1', '2', '2', '2', '2', '2', '2', '1', ' ',' ',' '); last SWITCH; };
       /B|BMaj|C#Dor|D#Phr|ELyd|F#Mix|G#m|G#Min|G#Aeo|A#Loc/   && do { @KeyNotes = ('2', '2', '1', '2', '2', '2', '1', '2', '2', '1', '2', '2', '2', '1', ' ',' ',' '); last SWITCH; };
       /E|EMaj|F#Dor|G#Phr|ALyd|BMix|C#m|C#Min|C#Aeo|D#Loc/    && do { @KeyNotes = ('2', '2', '1', '2', '2', '1', '1', '2', '2', '1', '2', '2', '1', '1', ' ',' ',' '); last SWITCH; };
	/A|AMaj|BDor|C#Phr|DLyd|EMix|F#m|F#Min|F#Aeo|G#Loc/    && do { @KeyNotes = ('2', '1', '1', '2', '2', '1', '1', '2', '1', '1', '2', '2', '1', '1', ' ',' ',' '); last SWITCH; };
	/D|DMaj|EDor|F#Phr|GLyd|AMix|Bm|BMin|BAeo|C#Loc|HP|Hp/ && do { @KeyNotes = ('2', '1', '1', '2', '1', '1', '1', '2', '1', '1', '2', '1', '1', '1', ' ',' ',' '); last SWITCH; };
	 /G|GMaj|ADor|BPhr|CLyd|DMix|Em|EMin|EAeo|F#Loc/       && do { @KeyNotes = ('1', '1', '1', '2', '1', '1', '1', '1', '1', '1', '2', '1', '1', '1', ' ',' ',' '); last SWITCH; };
	 /C|CMaj|DDor|EPhr|FLyd|GMix|Am|AMin|AAeo|BLoc/        && do { @KeyNotes = ('1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', ' ',' ',' '); last SWITCH; };
	/F|FMaj|GDor|APhr|BbLyd|CMix|Dm|DMin|DAeo|ELoc/        && do { @KeyNotes = ('1', '1', '1', '1', '1', '1', '0', '1', '1', '1', '1', '1', '1', '0', ' ',' ',' '); last SWITCH; };
      /Bb|BbMaj|CDor|DPhr|EbLyd|FMix|Gm|GMin|GAeo|ALoc/        && do { @KeyNotes = ('1', '1', '0', '1', '1', '1', '0', '1', '1', '0', '1', '1', '1', '0', ' ',' ',' '); last SWITCH; };
      /Eb|EbMaj|FDor|GPhr|AbLyd|BbMix|Cm|CMin|CAeo|DLoc/       && do { @KeyNotes = ('1', '1', '0', '1', '1', '0', '0', '1', '1', '0', '1', '1', '0', '0', ' ',' ',' '); last SWITCH; };
     /Ab|AbMaj|BbDor|CPhr|DbLyd|EbMix|Fm|FMin|FAeo|GLoc/       && do { @KeyNotes = ('1', '0', '0', '1', '1', '0', '0', '1', '0', '0', '1', '1', '0', '0', ' ',' ',' '); last SWITCH; };
     /Db|DbMaj|EbDor|FPhr|GbLyd|AbMix|Bbm|BbMin|BbAeo|CLoc/    && do { @KeyNotes = ('1', '0', '0', '1', '0', '0', '0', '1', '0', '0', '1', '0', '0', '0', ' ',' ',' '); last SWITCH; };
    /Gb|GbMaj|AbDor|BbPhr|CbLyd|DbMix|Ebm|EbMin|EbAeo|FLoc/    && do { @KeyNotes = ('0', '0', '0', '1', '0', '0', '0', '0', '0', '0', '1', '0', '0', '0', ' ',' ',' '); last SWITCH; };
    /Cb|CbMaj|DbDor|EbPhr|FbLyd|GbMix|Abm|AbMin|AbAeo|BbLoc/   && do { @KeyNotes = ('0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', ' ',' ',' '); last SWITCH; };
};
} # SetKey

##--------------------
#
# Configure String Handling
#
#----------------------
sub ReadDefinitions {
printf "\tVERBOSE:Handling String Definitions\n" if $verbose;

#
# Over-ride if guitar switch thrown
#
$RDef[1] = "E B, G, D, A,, E,," if $GuitarTab;
$RDef[2] = "Guitar"          if $GuitarTab;

$InstDesc = $RDef[2];
$DefaultStrings = $RDef[1];
(@StringDef) = split(/ /,$DefaultStrings);
$NumStrings = $#StringDef + 1;
printf "\tVERBOSE:Number of Strings:%s\n", $NumStrings if $verbose;

for ($StringGrp=0; $StringGrp <= $NumStrings - 1; $StringGrp++) {
    $StrDef = @StringDef[$StringGrp];
    $NumChar = length($StrDef);
    for ($sg=0; $sg < $NumChar; $sg++) {
	$StringNote = substr($StrDef,$sg,1);
	SWITCH: for ($StringNote) {
	    /\^/          && do { $StringNoteNumber[$StringGrp]++;                                           last SWITCH; };
	    /\_/          && do { $StringNoteNumber[$StringGrp]--;                                           last SWITCH; };
	    /\=/          && do { $StringNoteNumber[$StringGrp] = 0;                                         last SWITCH; };
	    /\,/          && do { $StringNoteNumber[$StringGrp] = $StringNoteNumber[$StringGrp] - 12;        last SWITCH; };
	    /\'/          && do { $StringNoteNumber[$StringGrp] = $StringNoteNumber[$StringGrp] + 12;        last SWITCH; };
	    /[A-Ga-g]/    && do { $StringNoteNumber[$StringGrp] = $NoteNum[index($NoteXlt,$StringNote)];     last SWITCH; };
	};
    }
    printf "\tVerbose:StringNoteNumber %s = %s\n", $StringGrp, $StringNoteNumber[$StringGrp] if $verbose;
}
for ($StringGrp=1; $StringGrp <= $NumStrings-1; $StringGrp++) {
    $StringDelt[$StringGrp] = $StringNoteNumber[$StringGrp]     - $StringNoteNumber[$StringGrp - 1];
    $StringDelt[$StringGrp] = $StringNoteNumber[$StringGrp - 1] - $StringNoteNumber[$StringGrp] if $GuitarTab;
    printf "\tVERBOSE:This Note:$StringNoteNumber[$StringGrp - 1] Next Note:$StringNoteNumber[$StringGrp] Difference:$StringDelt[$StringGrp]\n" if $verbose;
    if ($StringDelt[$StringGrp] < 0) {
	printf "\n***WARNING***String Definition problem in strings.cfg\nCheck for proper note definition (Capitalization??)\n\n";
    }
}
#$StringDelt[$NumStrings] = 24;
$HaveStringDef = "Yes";
&SetTab($NumStrings);
} # ReadDefinitions

##--------------------
#
# Clean up Variables
#
#----------------------
sub Initialize {

    printf "\tVERBOSE:Initializing Variables\n" if $verbose;
    $BottomLine = 1;
    $BottomLine = 11 if $GuitarTab;
    $DefLnthOff = 2;
    $LnthExpl = 0;
    $InChord = No;
    $Gracing = No;
    $Tie = 0;
    $NoTitle = "TRUE";
    $InHeader = "No";
    $InSong = "No";
    $HaveStringDef = "No";
    $HaveTimeSig = "No";
    $InComment = "No";
    $Accidental = 0;
    $Octave = 0;
    for ($i=0; $i<=26; $i++) {
	$information[$i] = "";
    }
} # Initialize

##--------------------
#
# Once per execution
#
#----------------------
##--------------------
#
# Handle Command Line Options
#
#----------------------
;# getopts.pl - a better getopt.pl

;# Usage:
;#      do Getopts('a:bc');  # -a takes arg. -b & -c not. Sets opt_* as a
;#                           #  side effect.

sub Getopts {
    local($argumentative) = @_;
    local(@args,$_,$first,$rest);
    local($errs) = 0;
    local($[) = 0;

    @args = split( / */, $argumentative );
    while(@ARGV && ($_ = $ARGV[0]) =~ /^-(.)(.*)/) {
	($first,$rest) = ($1,$2);
	$pos = index($argumentative,$first);
	if($pos >= $[) {
	    if($args[$pos+1] eq ':') {
		shift(@ARGV);
		if($rest eq '') {
		    ++$errs unless @ARGV;
		    $rest = shift(@ARGV);
		}
		eval "\$opt_$first = \$rest;";
	    }
	    else {
		eval "\$opt_$first = 1";
		if($rest eq '') {
		    shift(@ARGV);
		}
		else {
		    $ARGV[0] = "-$rest";
		}
	    }
	}
	else {
	    print STDERR "Unknown option: $first\n";
	    ++$errs;
	    if($rest ne '') {
		$ARGV[0] = "-$rest";
	    }
	    else {
		shift(@ARGV);
	    }
	}
    }
    $errs == 0;

} # Getopts


